

#include <chrono>
#include <string>
#include <iostream>

#include "cpp/utility.hpp"
#include "cpp/variant.hpp"

#include "catch.hpp"


using namespace cpp;


TEST_CASE("variant:basic")
{
	using namespace cpp;

	std::string s;

	variant<int, float, float, short > a(short(1));

	auto a1 = a;

	auto a2 = std::move(a1);


	REQUIRE(get<3>(a) == 1);
	REQUIRE(get<short>(a) == 1);


	REQUIRE_THROWS(get<0>(a));
	REQUIRE_THROWS(get<1>(a));
	REQUIRE_THROWS(get<2>(a));

	a = 1;

	a = short(1);

	variant<int, float, double > b0(in_place<int>, 10);
	variant<int, float, double > b1(in_place<float>, 10.f);
	variant<int, float, double > b2(in_place<double>, 10.);
	// 
	// 
	// 
	{
		variant<int, float, double > c0(in_place<0>, 10);
		variant<int, float, double > c11(in_place<1>, 10.f);
		variant<int, float, double > c2(in_place<2>, 10.);
	}

	{
		variant<int, float, double > c0(in_place<int>, 10);
		variant<int, float, double > c11(in_place<double>, 10.f);
		variant<int, float, double > c2(in_place<float>, 10.);
		get<int>(c0);
		auto cc0{ c0 };

		REQUIRE(cc0 == c0);
		c0.swap(c11);
		REQUIRE(c0.has_value());
	}

	variant <int, std::string, float> t1;

	REQUIRE(t1.index() == 0);

	t1 = "1111";
	REQUIRE(t1.index() == 1);

	t1 = 1.f;
	REQUIRE(t1.index() == 2);



	//////////////////////////////////////////////////////////////////////////
	struct Throw
	{
		Throw(int)
		{
			throw 0;
		}
		Throw(long)
		{

		}

		Throw(const Throw&)
		{
			throw std::runtime_error("");
		}
		~Throw() {}
	};


	variant<int, Throw> throw0;

	REQUIRE(throw0.index() == 0);

	REQUIRE_THROWS(throw0.emplace<1>(0));

	REQUIRE(throw0.valueless_by_exception());
	REQUIRE(throw0.index() == cpp::variant_npos);

	throw0.emplace<0>(555l);
	throw0.emplace<1>(555l);

	REQUIRE(throw0.index() == 1);

	try
	{
		auto throw1 = throw0;
		REQUIRE(false);
	}
	catch(const std::runtime_error&)
	{

	}


}


TEST_CASE("variant:alloc")
{

	variant <int, std::string> v0(std::allocator_arg, std::allocator<int>(), "aaa");
	REQUIRE(get<std::string>(v0) == "aaa");


	variant <int, std::string> v1(std::allocator_arg, std::allocator<int>(), 1);
	REQUIRE(get<int>(v1) == 1);

}

TEST_CASE("variant:visit")
{

	variant <int, std::string> t0 = 1111;
	variant <int, std::string> t1 = "bbbbb";

	visit([](auto&&  value0)
	{
		std::cout << value0 << std::endl;
	}, t0);


	visit([](auto&&  value0, auto&& value1)
	{
		std::cout << value0 << "," << value1 << std::endl;
	}, t0, t1);


#if 0
	auto n = std::chrono::high_resolution_clock::now();
	for(auto i = 0; i < 1'0000'0000; i++)
	{
		visit([](auto&& value0, auto&& value1)
		{

		}, t0, t1);

	}
	auto nn = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now() - n).count();

	std::cout << nn << "us";
#endif


	cpp::variant<int, const char*, double> v;
	v = 10.5;
	struct Vis
	{
		int operator()(int){ return 0; }
		int operator()(const char*){ return 1; }
		int operator()(double){ return 2; }
	} vvv;

	auto r = cpp::visit(vvv, v);
	REQUIRE(r == 2);

	v = 10;
	r = cpp::visit(vvv, v);
	REQUIRE(r == 0);

	v = "1";
	r = cpp::visit(vvv, v);
	REQUIRE(r == 1);
}
